Skip to content

feat: MCP app sampling support via stock SDK types (alt to #477)#530

Merged
ochafik merged 5 commits intomainfrom
claude/review-mcp-sampling-CkbHh
Apr 21, 2026
Merged

feat: MCP app sampling support via stock SDK types (alt to #477)#530
ochafik merged 5 commits intomainfrom
claude/review-mcp-sampling-CkbHh

Conversation

@ochafik
Copy link
Copy Markdown
Contributor

@ochafik ochafik commented Mar 6, 2026

Alternative to #477. Instead of forking a stripped-down sampling schema, this reuses the stock CreateMessageRequest / CreateMessageResult / CreateMessageResultWithTools types from @modelcontextprotocol/sdk directly — the same pattern already used for tools/call, resources/read, etc.

Motivation

#477 introduces custom McpUiSampling* types that diverge from core MCP sampling:

Core MCP #477
maxTokens required optional
tools / toolChoice ✅ (SEP-1577)
Message content block | block[] single block only
Content types text, image, audio, tool_use, tool_result text, image only
Result content any type, array ok {type:"text"} only
Capability shape sampling: { tools?: {} } sampling: { supportedModalities?: (...)[] }

The original PR's headline use case ("nested agents") requires tool-calling loops, which that schema can't represent.

Approach

Treat sampling/createMessage as a Standard MCP Message (spec § Standard MCP Messages), exactly like tools/call:

  • Spec (specification/draft/apps.mdx):

    • sampling/createMessage listed under Standard MCP Messages, referencing the upstream CreateMessageRequest shape (incl. SEP-1577 tools).
    • sampling?: { tools?: {} } added to HostCapabilities — mirrors MCP ClientCapabilities.sampling so hosts that are already MCP clients can pass it straight through.
  • Types (src/types.ts, src/spec.types.ts):

    • CreateMessageRequestAppRequest union
    • CreateMessageResult / CreateMessageResultWithToolsAppResult union
    • No new Zod schemas to maintain — imported from the SDK.
  • SDK ergonomics:

    • App.createSamplingMessage(params) — overloaded: returns CreateMessageResult when tools is absent, CreateMessageResultWithTools when present. Picks the correct result schema at runtime.
    • AppBridge.oncreatesamplingmessage = async (params, extra) => {...} — same setter pattern as oncalltool.
    • App.assertCapabilityForMethod now checks hostCapabilities.sampling (effective when enforceStrictCapabilities: true).
  • Tests & docs:

    • Round-trip integration test in app-bridge.test.ts.
    • Type-checked .examples.ts regions for both App and AppBridge JSDoc.

What we get for free

  • maxTokens required (cost-control guard rail)
  • temperature, stopSequences, modelPreferences, metadata
  • tools / toolChoice / tool_use / tool_result content blocks
  • stopReason: "toolUse" + array content for parallel tool calls
  • Audio content blocks
  • _meta on messages
  • Wire-compatible with hosts that already implement MCP sampling — the bridge can literally forward the request to an existing sampling/createMessage handler.

https://claude.ai/code/session_01ENGWTtsfcyP4S6fTWtUMAh

Alternative to #477. Instead of forking a stripped-down sampling schema,
this reuses the stock `CreateMessageRequest` / `CreateMessageResult` /
`CreateMessageResultWithTools` types from `@modelcontextprotocol/sdk`
directly — same pattern already used for `tools/call`.

- Spec: `sampling/createMessage` listed under `### Standard MCP Messages`,
  `sampling?: { tools?: {} }` added to `HostCapabilities` (mirrors MCP
  `ClientCapabilities.sampling` shape for easy pass-through).
- SDK: `App.createSamplingMessage()` with overloads that narrow the return
  type based on whether `params.tools` is set; `AppBridge.oncreatesamplingmessage`
  setter; `CreateMessageRequest`/`CreateMessageResult*` added to the
  `AppRequest`/`AppResult` unions.
- Picks up SEP-1577 tool-calling support (`tools`, `toolChoice`, `tool_use`
  content blocks, `stopReason: "toolUse"`, array content) for free — unblocks
  the "nested agents" use case motivated by the original PR.

https://claude.ai/code/session_01ENGWTtsfcyP4S6fTWtUMAh
Comment thread src/app-bridge.examples.ts
Comment thread src/app.examples.ts Fixed
Comment thread src/app.examples.ts Fixed
@alexhancock
Copy link
Copy Markdown

@ochafik this seems like a clean approach to me. do you have things you want to do on it before we seek review?

when ready, I can update goose to use oncreatesamplingmessage instead of the fallback.

cc @liady @idosal @aharvard

…pling-CkbHh

# Conflicts:
#	src/app.examples.ts
@ochafik ochafik requested review from idosal, liady and localden April 21, 2026 10:21
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 21, 2026

Preview

Preview deployments for this PR have been cleaned up.

Comment thread src/app.examples.ts
Comment thread src/app.examples.ts
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 21, 2026

Open in StackBlitz

@modelcontextprotocol/ext-apps

npm i https://pkg.pr.new/@modelcontextprotocol/ext-apps@530

@modelcontextprotocol/server-basic-preact

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-preact@530

@modelcontextprotocol/server-basic-react

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-react@530

@modelcontextprotocol/server-basic-solid

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-solid@530

@modelcontextprotocol/server-basic-svelte

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-svelte@530

@modelcontextprotocol/server-basic-vanillajs

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-vanillajs@530

@modelcontextprotocol/server-basic-vue

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-vue@530

@modelcontextprotocol/server-budget-allocator

npm i https://pkg.pr.new/@modelcontextprotocol/server-budget-allocator@530

@modelcontextprotocol/server-cohort-heatmap

npm i https://pkg.pr.new/@modelcontextprotocol/server-cohort-heatmap@530

@modelcontextprotocol/server-customer-segmentation

npm i https://pkg.pr.new/@modelcontextprotocol/server-customer-segmentation@530

@modelcontextprotocol/server-debug

npm i https://pkg.pr.new/@modelcontextprotocol/server-debug@530

@modelcontextprotocol/server-map

npm i https://pkg.pr.new/@modelcontextprotocol/server-map@530

@modelcontextprotocol/server-pdf

npm i https://pkg.pr.new/@modelcontextprotocol/server-pdf@530

@modelcontextprotocol/server-scenario-modeler

npm i https://pkg.pr.new/@modelcontextprotocol/server-scenario-modeler@530

@modelcontextprotocol/server-shadertoy

npm i https://pkg.pr.new/@modelcontextprotocol/server-shadertoy@530

@modelcontextprotocol/server-sheet-music

npm i https://pkg.pr.new/@modelcontextprotocol/server-sheet-music@530

@modelcontextprotocol/server-system-monitor

npm i https://pkg.pr.new/@modelcontextprotocol/server-system-monitor@530

@modelcontextprotocol/server-threejs

npm i https://pkg.pr.new/@modelcontextprotocol/server-threejs@530

@modelcontextprotocol/server-transcript

npm i https://pkg.pr.new/@modelcontextprotocol/server-transcript@530

@modelcontextprotocol/server-video-resource

npm i https://pkg.pr.new/@modelcontextprotocol/server-video-resource@530

@modelcontextprotocol/server-wiki-explorer

npm i https://pkg.pr.new/@modelcontextprotocol/server-wiki-explorer@530

commit: a5e5319

@ochafik ochafik requested review from antonpk1 and removed request for localden April 21, 2026 10:43
@ochafik ochafik merged commit 926dcb4 into main Apr 21, 2026
22 checks passed
ochafik added a commit that referenced this pull request Apr 21, 2026
Conflict resolution:
- src/app.ts: kept _registeredTools (#72) alongside main's fields; took
  main's assertCapabilityForMethod implementation (sampling check from #530).
- src/react/useApp.tsx: took main's version (#72 doesn't touch this file;
  main has #622's autoResize forwarding).
- typedoc.config.mjs: kept #72's RequestHandlerExtra in
  intentionallyNotExported.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants